<?
/**
 * Copyright 2007 Melange.
 *
 * This file is part of PHP-MVC.
 *
 * PHP-MVC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP-MVC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP-MVC; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category    Melange
 * @package     php-mvc
 * @subpackage  util
 * @copyright   Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @author      Jeroen Simons <jeroen@melange.nl>
 * @link        http://www.melange.nl/
 *
 */

/**
 * TokenProcessor is responsible for handling all token related functionality.
 * The methods in this class are synchronized to protect token processing from
 * multiple threads.  Servlet containers are allowed to return a different
 * HttpSession object for two threads accessing the same session so it is not
 * possible to synchronize on the session.
 *
 * @since Struts 1.1
 */
class TokenProcessor {


    /**
     * The singleton instance of this class.
     */
    private static $instance;


    /**
     * The timestamp used most recently to generate a token value.
     */
    private $previous;


    /**
     * Retrieves the singleton instance of this class.
     */
    public function getInstance() {

        if(self::$instance === null)
             self::$instance = new TokenProcessor();

        return self::$instance;
    }


    /**
     * Return <code>true</code> if there is a transaction token stored in the
     * user's current session, and the value submitted as a request parameter
     * with this action matches it.  Returns <code>false</code>
     *
     * <ul>
     *
     * <li>No session associated with this request</li> <li>No transaction
     * token saved in the session</li>
     *
     * <li>No transaction token included as a request parameter</li>
     *
     * <li>The included transaction token value does not match the transaction
     * token in the user's session</li>
     *
     * </ul>
     *
     * @param request The servlet request we are processing
     * @param reset   Should we reset the token after checking it?
     */
    public function isTokenValid(Request $request,
        $reset = false) {

        // Retrieve the current session for this request
        $session = $request->getSession(false);

        if ($session === null) {
            return false;
        }

        // Retrieve the transaction token from this session, and
        // reset it if requested
        $saved = $session->getAttribute(MVC::TRANSACTION_TOKEN_KEY);

        if ($saved === null) {
            return false;
        }

        if ($reset) {
            $this->resetToken($request);
        }

        // Retrieve the transaction token included in this request
        $token = $request->getParameter(MVC::TOKEN_KEY);

        if ($token === null) {
            return false;
        }

        return $saved == $token;
    }

    /**
     * Reset the saved transaction token in the user's session.  This
     * indicates that transactional token checking will not be needed on the
     * next request that is submitted.
     *
     * @param request The servlet request we are processing
     */
    public function resetToken(Request $request) {
        $session = $request->getSession(false);

        if ($session === null) {
            return;
        }

        $session->removeAttribute(MVC::TRANSACTION_TOKEN_KEY);
    }

    /**
     * Save a new transaction token in the user's current session, creating a
     * new session if necessary.
     *
     * @param request The servlet request we are processing
     */
    public function saveToken(Request $request) {
        $session = $request->getSession();
        $token = $this->generateToken($request);
        if ($token !== null) {
            $session->setAttribute(MVC::TRANSACTION_TOKEN_KEY, $token);
        }
    }

    /**
     * Generate a new transaction token, to be used for enforcing a single
     * request for a particular transaction.
     *
     * @param request The request we are processing
     */
    public function generateToken(Request $request) {
        $session = $request->getSession();

        return $this->_generateToken($session->getId());
    }

    /**
     * Generate a new transaction token, to be used for enforcing a single
     * request for a particular transaction.
     *
     * @param id a unique Identifier for the session or other context in which
     *           this token is to be used.
     */
    public function _generateToken($id) {

        $current = time();

        if ($current == $this->previous) {
            $current++;
        }

        $this->previous = $current;

        $now = md5(time());

        return $now;
    }

}

?>